package db;

import java.net.UnknownHostException;
import java.sql.Date;
import java.util.LinkedList;
import java.util.List;

import server.ServerConstants;
import simulation.SimulationConstants;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

import entities.Location;
import entities.Message;

public class MongoDBDAO extends MessageDAO {
	private DB db = null;
	private Mongo m = null;
	private DBCollection coll = null;
	
	@Override
	protected void dbConnect() {
		try {
			m = new Mongo(ServerConstants.IP_ADDRESS, ServerConstants.MONGO_PORT);
			db = m.getDB(ServerConstants.MONGO_DB);
			coll = db.getCollection(ServerConstants.MONGO_COLLECTION);
		}
		catch (UnknownHostException e) {
			e.printStackTrace();
		}
		catch (MongoException e) {
			e.printStackTrace();
		}
	}

	@Override
	protected void dbCloseConnection() {
	}

	@Override
	protected void dbAddMessage(Message message) {
		BasicDBObject doc = new BasicDBObject();

        doc.put("id", message.getId());
        doc.put("senderId", message.getSenderId());
        
        BasicDBList location = new BasicDBList();
        location.add(message.getLocation().getLatitude());
        location.add(message.getLocation().getLongitude());
        doc.put("location", location);

        doc.put("timeOfOrigin", message.getTimeOfOrigin());
        doc.put("timeOfExpiry", message.getTimeOfExpiry());
        doc.put("message", message.getMessage());
        
        doc.put("recipients", new LinkedList<Integer>());
        
        coll.insert(doc);
	}

	@Override
	protected List<Message> dbGetMessagesForRecipientLocation(int recipientId,
			Location location) {
		DBObject query = new BasicDBObject();

		// Only messages that this recipient has not already received.
		query.put("recipients", new BasicDBObject("$ne", recipientId));
		
		// Add the latitude and longitude to a JSON array, to be used with the $near condition.
		BasicDBList locationList = new BasicDBList();
		locationList.add(location.getLatitude());
		locationList.add(location.getLongitude());
		
		BasicDBObject locationConditions = new BasicDBObject();
		// Only messages that originated close to the recipient's location.
		locationConditions.put("$near", locationList);
		locationConditions.put("$maxDistance", SimulationConstants.MESSAGE_RANGE);
		
		query.put("location", locationConditions);
		DBCursor cur = coll.find(query);
		List<Message> messages = new LinkedList<Message>();
        while (cur.hasNext()) {
        	DBObject dbObject = cur.next();
        	messages.add(createMessageFromDBObject(dbObject));
        	query = new BasicDBObject("$push", new BasicDBObject("recipients", recipientId));
        	coll.update(cur.curr(), query);
        }
		return messages;
	}

	@Override
	protected List<Message> dbGetAllMessages() {
		DBCursor cur = coll.find();
		List<Message> messages = new LinkedList<Message>();
        while(cur.hasNext()) {
        	DBObject dbObject = cur.next();
        	messages.add(createMessageFromDBObject(dbObject));
        }
		return messages;
	}
	
	private Message createMessageFromDBObject(DBObject dbObject) {
		Double latitude = (Double)((DBObject)dbObject.get("location")).get("0");
    	Double longitude = (Double)((DBObject)dbObject.get("location")).get("1");
    	Message msg =
    			new Message((Integer)dbObject.get("id"),
    			(Integer)dbObject.get("senderId"),
    			new Location(latitude, longitude),
    			new Date(((java.util.Date)dbObject.get("timeOfOrigin")).getTime()),
    			new Date(((java.util.Date)dbObject.get("timeOfExpiry")).getTime()),
    			(String)dbObject.get("message"));
    	return msg;
	}

	@Override
	protected void dbClearDatabase() {
		coll.remove(new BasicDBObject());
	}
}